/*
 * the codes was writted by taing, but you can copy or moidfy it
 */

package com.taing;

import com.taing.autosql.sqlconfig.IAutoSqlDao;
import com.taing.autosqlutils.ServiceConfig;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @Author tianyi
 * @Description mybatis的拦截类
 * @Date 2023 - 01 - 30
 **/
@Intercepts(@Signature(type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class}))
public class AutoSqlIntercepter implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler target = (StatementHandler) invocation.getTarget();
        String fieldDelegate = "delegate";
        Object delegate = FieldUtils.readField(target, fieldDelegate, true);
        MappedStatement mappedStatement = (MappedStatement) FieldUtils.readField(delegate, "mappedStatement", true);
        String sqlId = mappedStatement.getId();
        String packageName = "com.taing.autosql.sqlconfig.IAutoSqlDao";
        if (sqlId.contains(packageName)) {
            // 取出sql
            BoundSql boundSql = target.getBoundSql();
            // 获得用户的参数
            Map<String, Object> paramObject = (Map<String, Object>) boundSql.getParameterObject();
            // get the method from the paramter, if you can't find it ,the show error
            String serviceConfigMethod = "methodName";
            String method = (String) paramObject.get(serviceConfigMethod);
            if (null == method || method.trim().length() == 0) {
                throw new IllegalArgumentException("the paramter can't find the" + serviceConfigMethod);

            }
            Configuration configuration = new Configuration();
            ServiceConfig serviceConfig = new ServiceConfig(configuration, IAutoSqlDao.class);
            serviceConfig.parse(method);
            MappedStatement ms = configuration.getMappedStatement(sqlId);
            BoundSql newBoundSql = ms.getBoundSql(boundSql.getParameterObject());
            List<ParameterMapping> parameterMetaDataList = newBoundSql.getParameterMappings();
            String sql = newBoundSql.getSql();
            FieldUtils.writeField(boundSql, "sql", sql, true);
            String fieldName = "additionalParameters";
            Object parameter = FieldUtils.readField(newBoundSql,
                    fieldName, true);
            Map<String, Object> additionalParamters = parameter instanceof Map<?, ?> ? (Map<String, Object>) parameter : null;
            if (additionalParamters != null) {
                for (Map.Entry<String, Object> paramItem : additionalParamters.entrySet()) {
                    if (paramItem != null) {
                        boundSql.setAdditionalParameter(paramItem.getKey(), paramItem.getValue());
                        MapUtils.putAll(paramObject, new Object[]{paramItem});
                    }
                }
            }
            FieldUtils.writeField(boundSql, "parameterMappings", parameterMetaDataList, true);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
